package com.tang.common.utils;


import cn.hutool.core.util.PageUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import cn.hutool.json.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tang.common.constant.BusinessConstant;
import com.tang.common.exception.GlobalException;
import com.tang.common.resources.BaseEntity;
import com.tang.common.resources.TaoPage;
import com.tang.module.system.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 工具类
 * @author tang
 * @date 2021/10/25 13:29
 */
@Slf4j
public class CommonUtils {

    /**
     * 将毫秒值换算成对应的单位
     * @param millisecond 毫秒值
     * @return 换算后的值
     */
    public static String timeDisplayString(Long millisecond){
        if (millisecond < 1000){
            return millisecond + "毫秒";
        }
        if (millisecond < 1000 * 60){
            return millisecond / 1000 + "秒";
        }
        if (millisecond < 1000 * 60 * 60){
            return millisecond / (1000 * 60) + "分钟";
        }
        if (millisecond < 1000 * 60 * 60 * 60){
            return millisecond / (1000 * 60 * 60 )+ "小时";
        }
        return  millisecond / (1000 * 60 * 60 * 60) + "天";
    }

    /**
     * 根据请求获取真实的IP地址
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = null;


        //X-Forwarded-For：Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");


        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP：apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }


        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP：weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }


        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP：有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }


        if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP：nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }


        //有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }


        //还是不能获取到，最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        if ("0:0:0:0:0:0:0:1".equalsIgnoreCase(ip)){
            ip = "127.0.0.1";
        }
        return ip;
    }

    /**
     * 开始分页
     * @param list 需要分页的数据
     * @param current 页码
     * @param pageSize 每页多少条数据
     * @return 分页对象
     */
    public static <T>  Page<T> pageUtli(List<T> list, Integer current, Integer pageSize) {
        //数据为空
        if (list == null ||list.size() == 0) {
           return new Page<T>(current,pageSize, 0);
        }
        int totalPage = PageUtil.totalPage(list.size(), pageSize);
        //当前页码是否超过总页码数
        if (current > totalPage){
            return new Page<T>(current,pageSize,list.size());
        }
        //计算开始和结束位置，输入的页码从1开始
        int[] ints = PageUtil.transToStartEnd(current - 1, pageSize);
        //mybatis分页对象
        Page<T> page = new Page<>(current,pageSize,list.size());
        if (totalPage == 1){
            page.setRecords(list);
        }
        //是否为最后一页
        if (current == totalPage){
            page.setRecords(list.subList(ints[0], list.size()));
        }
        return page;
    }

    /**
     * 填充模板参数
     * @param template 模板字符串
     * @param paramMap 参数map
     * @return 填充好的字符串
     */
    public static String fillTemplateParam(String template,Map<String,String> paramMap){
        //模板引擎，设置模板
        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig());
        Template engineTemplate = engine.getTemplate(template);
        if (paramMap == null || paramMap.size() == 0){
            throw new GlobalException("解析模板参数异常，模板参数为空");
        }
        return engineTemplate.render(paramMap);
    }

    private static Pattern linePattern = Pattern.compile("_(\\w)");

    /**
     * 下划线转驼峰
     * @param str 下划线字段
     * @return
     */
    public static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }


    private static Pattern humpPattern = Pattern.compile("[A-Z]");

    /**
     * 峰转下划线
     * @param str 驼峰字段
     * @return
     */
    public static String humpToLine(String str) {
        Matcher matcher = humpPattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 排序查询
     * @param queryWrapper mybatis-plus查询对象
     * @param taoPage 系统分页排序参数
     * @param <T> 实体类类型
     * @return 添加排序规则的查询对象
     */
    public static <T>  QueryWrapper<T>  orderQueryWrapper(QueryWrapper<T> queryWrapper,TaoPage taoPage){
        if (BusinessConstant.ORDER_DESCENDING.equals(taoPage.getOrder())){
            queryWrapper.orderByDesc(CommonUtils.humpToLine(taoPage.getSortField()));
        }
        if (BusinessConstant.ORDER_ASCENDING.equals(taoPage.getOrder())){
            queryWrapper.orderByAsc(CommonUtils.humpToLine(taoPage.getSortField()));
        }
        return queryWrapper;
    }

    /**
     * 新增时，填充创建/更新用户和时间
     * @param data 新增实体
     * @param user 当前用户
     * @return
     */
    public static BaseEntity baseEntityField(BaseEntity data, User user){
        data.setCreateUser(user.getId());
        data.setUpdateUser(user.getId());
        data.setCreateDept(Long.valueOf(user.getDeptId()));
        data.setCreateTime(new Date());
        data.setUpdateTime(new Date());
        return data;
    }

    /**
     * 格式化执行时间，单位为 ms 和 s，保留三位小数
     *
     * @param nanos 纳秒
     * @return 格式化后的时间
     */
    public static String format(long nanos) {
        if (nanos < 1) {
            return "0ms";
        }
        double millis = (double) nanos / (1000 * 1000);
        // 不够 1 ms，最小单位为 ms
        if (millis > 1000) {
            return String.format("%.3fs", millis / 1000);
        } else {
            return String.format("%.3fms", millis);
        }
    }

    /**
     * 去掉指定后缀
     *
     * @param str    字符串
     * @param suffix 后缀
     * @return 切掉后的字符串，若后缀不是 suffix， 返回原字符串
     */
    public static String removeSuffix(CharSequence str, CharSequence suffix) {
        if (StringUtils.isEmpty(str) || StringUtils.isEmpty(suffix)) {
            return StringPool.EMPTY;
        }

        final String str2 = str.toString();
        if (str2.endsWith(suffix.toString())) {
            return subPre(str2, str2.length() - suffix.length());
        }
        return str2;
    }

    /**
     * 切割指定位置之前部分的字符串
     *
     * @param string  字符串
     * @param toIndex 切割到的位置（不包括）
     * @return 切割后的剩余的前半部分字符串
     */
    public static String subPre(CharSequence string, int toIndex) {
        return sub(string, 0, toIndex);
    }

    /**
     * 改进JDK subString<br>
     * index从0开始计算，最后一个字符为-1<br>
     * 如果from和to位置一样，返回 "" <br>
     * 如果from或to为负数，则按照length从后向前数位置，如果绝对值大于字符串长度，则from归到0，to归到length<br>
     * 如果经过修正的index中from大于to，则互换from和to example: <br>
     * abcdefgh 2 3 =》 c <br>
     * abcdefgh 2 -3 =》 cde <br>
     *
     * @param str       String
     * @param fromIndex 开始的index（包括）
     * @param toIndex   结束的index（不包括）
     * @return 字串
     */
    public static String sub(CharSequence str, int fromIndex, int toIndex) {
        if (StringUtils.isEmpty(str)) {
            return StringPool.EMPTY;
        }
        int len = str.length();

        if (fromIndex < 0) {
            fromIndex = len + fromIndex;
            if (fromIndex < 0) {
                fromIndex = 0;
            }
        } else if (fromIndex > len) {
            fromIndex = len;
        }

        if (toIndex < 0) {
            toIndex = len + toIndex;
            if (toIndex < 0) {
                toIndex = len;
            }
        } else if (toIndex > len) {
            toIndex = len;
        }

        if (toIndex < fromIndex) {
            int tmp = fromIndex;
            fromIndex = toIndex;
            toIndex = tmp;
        }

        if (fromIndex == toIndex) {
            return StringPool.EMPTY;
        }

        return str.toString().substring(fromIndex, toIndex);
    }

    /**
     * 判断是否可以序列化
     *
     * @param value 对象
     * @return 是否可以序列化
     */
    public static boolean canSerialize(@Nullable Object value) {
        if (value == null) {
            return true;
        }
        return new ObjectMapper().canSerialize(value.getClass());
    }

    /**
     * 获得当前登录用户
     * @return 当前用户
     */
    public static User getLoginUser(){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || authentication.getPrincipal() == null){
            throw new GlobalException("无法获取登录用户");
        }
        //登录用户
        return  (User) authentication.getPrincipal();
    }

}
